package com.lx.pc.service;

import java.io.Serializable;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mybatis.repository.support.MybatisRepository;

import com.lx.pc.domain.entity.AbstractBaseEntity;
import com.lx.pc.service.exception.DataDeleteException;
import com.lx.pc.service.exception.DataFindException;
import com.lx.pc.service.exception.DataSaveException;
import com.lx.pc.service.exception.DataUpdateException;
import com.lx.pc.utils.Utils;

/**
 * 抽象服务
 * 
 * @author martin_duan@yeah.net
 * @since 2017-03-20
 *
 * @param <E>
 *            entity
 * @param <V>
 *            vo
 * @param <ID>
 *            id
 * @param <R>
 *            repository
 */
@SuppressWarnings("unchecked")
public abstract class AbstractService<E extends AbstractBaseEntity, V extends E, ID extends Serializable, R extends MybatisRepository<E, ID>> implements IService<V, ID>, InitializingBean, ApplicationContextAware {

	protected Logger logger = LoggerFactory.getLogger(this.getClass());

	public static final String SYSTEM_ACCOUNT_USERNAME = "sys";

	public static final BigInteger SYSTEM_ACCOUNT_ID = new BigInteger("27633888652206342159160056938");

	protected Class<E> entityClass;

	protected Class<V> voClass;

	protected Class<ID> idClass;

	protected Class<R> repositoryClass;

	protected R repository;

	protected ApplicationContext context;

	protected SqlSessionTemplate sessionTemplate;

	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.context = context;
		if (context.containsBean("sqlSessionTemplateProject")) {
			sessionTemplate = context.getBean("sqlSessionTemplateProject", SqlSessionTemplate.class);
		}
	}

	/**
	 * 初始化仓库
	 * 
	 * @param repositoryName
	 */
	private void initRepository(String repositoryName) {
		if (repository == null && context.containsBean(repositoryName)) {
			repository = context.getBean(repositoryName, repositoryClass);
		}
	}

	/**
	 * 获得仓库名称
	 * 
	 * @return
	 */
	private String getRepositoryName() {
		return repositoryClass.getSimpleName().substring(0, 1).toLowerCase() + repositoryClass.getSimpleName().substring(1);
	}

	/**
	 * 初始化类
	 */
	private void initClass() {
		List<Class<?>> list = Utils.genericClass(this.getClass(), AbstractService.class);
		entityClass = (Class<E>) list.get(0);
		voClass = (Class<V>) list.get(1);
		idClass = (Class<ID>) list.get(2);
		repositoryClass = (Class<R>) list.get(3);
	}

	/**
	 * 初始化
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		initClass();
		String repositoryName = getRepositoryName();
		logger.info("repository name:{},entity class:{},vo class:{},id class:{},repository class:{},", repositoryName, entityClass, voClass, idClass, repositoryClass);
		initRepository(repositoryName);
	}

	/**
	 * 保存
	 */
	@Override
	public void save(V v) {
		if (v.getId() == null) {
			v.setId(Utils.id());
		}
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		v.setActive((short)1);
		v.setCreateTime(df.format(new Date()));
		v.setModifyTime(new Date());
		try {
			repository.insert(v);
		} catch (Exception e) {
			e.printStackTrace();
			v.setId(null);
			v.setCreateTime(null);
			v.setModifyTime(null);
			throw new DataSaveException(e);
		}
	}

	/**
	 * 批量保存
	 */
	@Override
	public Long save(List<V> list) {
		Long count = 0L;
		if (list != null && !list.isEmpty()) {
			for (V v : list) {
				try {
					save(v);
					count++;
				} catch (Exception e) {
					logger.error("保存错误", e);
				}
			}
		}
		return count;
	}

	/**
	 * 更新
	 */
	@Override
	public Integer update(V condition, V update) {
		if (update == null) {
			return 0;
		}
		try {
			if (condition == null) {
				condition = Utils.newInstanceClass(voClass);
			}
			
			update.setModifyTime(new Date());
			return repository.updateByEqCondition(condition, update);
		} catch (Exception e) {
			throw new DataUpdateException(e);
		}
	}

	@Override
	public Integer update(ID id, Long version, V update) {
		if (update == null) {
			return 0;
		}
		V condition = Utils.newInstanceClass(voClass);
		condition.setId(BigInteger.class.cast(id));
		return update(condition, update);
	}

	@Override
	public Integer delete(ID id, Long version, Boolean isLogicDelete) {
		try {
			if (isLogicDelete == null) {
				isLogicDelete = Boolean.TRUE;
			}
			V condition = Utils.newInstanceClass(voClass);
			condition.setId(BigInteger.class.cast(id));
				
				return repository.deleteByCondition(condition);
		} catch (Exception e) {
			throw new DataDeleteException(e);
		}
	}

	/**
	 * 根据ID查找
	 */
	@Override
	public V find(ID id) {
		try {
			E entity = repository.findOne(id);
			if (entity != null) {
				V v = Utils.newInstanceClass(voClass);
				Utils.copyNotNullObject(entity, v);
				return v;
			}
			return null;
		} catch (Exception e) {
			throw new DataFindException(e);
		}
	}

	@Override
	public V find(ID id, Long version) {
		try {
			E entity = repository.findOne(id, version);
			if (entity != null) {
				V v = Utils.newInstanceClass(voClass);
				Utils.copyNotNullObject(entity, v);
				return v;
			}
			return null;
		} catch (Exception e) {
			throw new DataFindException(e);
		}
	}

	/**
	 * 查询总条数
	 */
	@Override
	public long count(V condition, boolean isEqCondition) {
		if (condition == null) {
			condition = Utils.newInstanceClass(voClass);
		}
		
		if (isEqCondition) {
			return repository.countByEqCondition(condition);
		}
		return repository.countByCondition(condition);
	}

	/**
	 * 分页查询
	 */
	@Override
	public Page<V> find(V condition, boolean isEqCondition, Pageable pageable) {
		if (condition == null) {
			condition = Utils.newInstanceClass(voClass);
		}
		if (isEqCondition) {
			return repository.findAllByEqCondition(pageable, condition).map(new DataConverter());
		}
		return repository.findAllByCondition(pageable, condition).map(new DataConverter());
	}

	/**
	 * 查找所有
	 */
	@Override
	public List<V> findAll(V condition, boolean isEqCondition) {
		if (condition == null) {
			condition = Utils.newInstanceClass(voClass);
		}
		
		List<E> list = null;
		if (isEqCondition) {
			list = repository.findAllByEqCondition(condition);
		} else {
			list = repository.findAllByCondition(condition);
		}
		return etoV(list);
	}

	/**
	 * 数据转换,E到V转换
	 * 
	 * @author martin_duan@yeah.net
	 * @since 2017-03-25
	 *
	 */
	public class DataConverter implements Converter<E, V> {

		@Override
		public V convert(E source) {
			V v = Utils.newInstanceClass(voClass);
			Utils.copyNotNullObject(source, v);
			return v;
		}

	}

	/**
	 * entity转vo
	 * 
	 * @param e
	 * @return
	 */
	protected V etoV(E e) {
		if (e == null) {
			return null;
		}
		V v = Utils.newInstanceClass(voClass);
		Utils.copyNotNullObject(e, v);
		return v;
	}

	/**
	 * 批量entity转vo
	 * 
	 * @param es
	 * @return
	 */
	protected List<V> etoV(List<E> es) {
		List<V> list = new ArrayList<V>();
		for (E e : es) {
			list.add(etoV(e));
		}
		return list;
	}

}
